<template><div><h2 id="模型" tabindex="-1"><a class="header-anchor" href="#模型"><span>模型</span></a></h2>
<p>模型是 <a href="https://zh.wikipedia.org/wiki/MVC" target="_blank" rel="noopener noreferrer">MVC</a> 模式中的一部分，是代表业务数据、规则和逻辑的对象。</p>
<p>可通过继承 <code v-pre>yii\base\Model</code> 或它的子类定义的模型类，基类 <code v-pre>yii\base\Model</code> 支持许多实用的特性：</p>
<ul>
<li><a href="#24d678">属性</a> ：代表可像普通类属性或数组 一样被访问的业务数据；</li>
<li><a href="#ec35f7">属性标签</a> ：指定属性显示出来的标签；</li>
<li><a href="#fa5663">块赋值</a> ：支持一步给许多属性赋值；</li>
<li><a href="#4eddee">验证规则</a> ：确保输入数据符合所申明的验证规则；</li>
<li><a href="#09d442">数据导出</a> ：允许模型数据导出为自定义格式的数组。</li>
</ul>
<p><code v-pre>Model</code> 类也是更多高级模型如 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md" target="_blank" rel="noopener noreferrer">Active Record（活动记录）</a> 的基类，更多关于这些高级模型的详情请参考相关手册。</p>
<blockquote>
<p>Info: 模型并不强制一定要继承 <code v-pre>yii\base\Model</code>，但是由于很多组件支持它，最好使用它做为模型基类。</p>
</blockquote>
<h2 id="属性" tabindex="-1"><a class="header-anchor" href="#属性"><span>属性</span></a></h2>
<p>模型通过 <em>属性</em> 来代表业务数据，每个属性像是模型的公有可访问属性， [[yii\base\Model::attributes()]] 指定模型所拥有的属性。</p>
<p>我们可像访问一个对象属性一样访问模型的属性，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>app<span class="token punctuation">\</span>models<span class="token punctuation">\</span>ContactForm</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// "name" 是ContactForm模型的属性</span></span>
<span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">name</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'example'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">echo</span> <span class="token variable">$model</span><span class="token operator">-></span><span class="token property">name</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>也可像访问数组单元项一样访问属性，这得益于 <code v-pre>yii\base\Model</code> 支持 <a href="https://www.php.net/manual/zh/class.arrayaccess.php" target="_blank" rel="noopener noreferrer">数组式访问</a> 和 <a href="https://www.php.net/manual/zh/class.arrayiterator.php" target="_blank" rel="noopener noreferrer">数组迭代器</a>，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>app<span class="token punctuation">\</span>models<span class="token punctuation">\</span>ContactForm</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 像访问数组单元项一样访问属性</span></span>
<span class="line"><span class="token variable">$model</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'name'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'example'</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">echo</span> <span class="token variable">$model</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'name'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 迭代器遍历模型</span></span>
<span class="line"><span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$model</span> <span class="token keyword">as</span> <span class="token variable">$name</span> <span class="token operator">=></span> <span class="token variable">$value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">echo</span> <span class="token string double-quoted-string">"<span class="token interpolation"><span class="token variable">$name</span></span>: <span class="token interpolation"><span class="token variable">$value</span></span>\n"</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="定义属性" tabindex="-1"><a class="header-anchor" href="#定义属性"><span>定义属性</span></a></h3>
<p>默认情况下你的模型类直接从 <code v-pre>yii\base\Model</code> 继承，所有 <em>非静态的且公有的</em> 成员变量都是属性。 例如，下述 <code v-pre>ContactForm</code> 模型类有四个属性<code v-pre>name</code>, <code v-pre>email</code>, <code v-pre>subject</code> and <code v-pre>body</code>，<code v-pre>ContactForm</code> 模型用来代表从 HTML 表单获取的输入数据。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>models</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>base<span class="token punctuation">\</span>Model</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">ContactForm</span> <span class="token keyword">extends</span> <span class="token class-name">Model</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$name</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$email</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$subject</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$body</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>另一种方式是可覆盖 [[yii\base\Model::attributes()]] 来定义属性，该方法返回模型的属性名。例如 <code v-pre>yii\db\ActiveRecord</code> 返回对应数据表列名作为它的属性名，注意可能需要覆盖魔术方法如 <code v-pre>__get()</code> 、<code v-pre>__set()</code> 等，使属性像普通对象属性一样被访问。</p>
<h3 id="属性标签" tabindex="-1"><a class="header-anchor" href="#属性标签"><span>属性标签</span></a></h3>
<p>当属性显示或获取输入时，经常要显示属性相关标签，例如，假定一个属性名为 <code v-pre>firstName</code>，在某些地方，如表单输入或错误信息处，你可能想显示对终端用户来说更友好的 <code v-pre>First Name</code> 标签。</p>
<p>您可以调用 [[yii\base\Model::getAttributeLabel()]] 获取属性的标签，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>app<span class="token punctuation">\</span>models<span class="token punctuation">\</span>ContactForm</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 显示为 "Name"</span></span>
<span class="line"><span class="token keyword">echo</span> <span class="token variable">$model</span><span class="token operator">-></span><span class="token function">getAttributeLabel</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'name'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>默认情况下，属性标签通过 [[yii\base\Model::generateAttributeLabel()]] 方法自动从属性名生成，它会自动将驼峰式大小写变量名转换为多个首字母大写的单词， 例如 <code v-pre>username</code> 转换为 <code v-pre>Username</code>， <code v-pre>firstName</code> 转换为 <code v-pre>First Name</code>。</p>
<p>如果你不想用自动生成的标签，可以覆盖 [[yii\base\Model::attributeLabels()]] 方法明确指定属性标签， 例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>models</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>base<span class="token punctuation">\</span>Model</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">ContactForm</span> <span class="token keyword">extends</span> <span class="token class-name">Model</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$name</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$email</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$subject</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$body</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">attributeLabels</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'name'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'Your name'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'email'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'Your email address'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'subject'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'Subject'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'body'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'Content'</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h4 id="国际化" tabindex="-1"><a class="header-anchor" href="#国际化"><span>国际化</span></a></h4>
<p>应用支持多语言的情况下，可翻译属性标签，可在 [[yii\base\Model::attributeLabels()]] 方法中定义，如下所示:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">attributeLabels</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'name'</span> <span class="token operator">=></span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'Your name'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token string single-quoted-string">'email'</span> <span class="token operator">=></span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'Your email address'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token string single-quoted-string">'subject'</span> <span class="token operator">=></span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'Subject'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token string single-quoted-string">'body'</span> <span class="token operator">=></span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'Content'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>甚至可以根据条件定义标签，例如通过使用模型的 <a href="#c93165">场景</a>，可对相同的属性返回不同的标签。</p>
<blockquote>
<p>Info: 属性标签是视图的一部分，但是在模型中声明标签通常非常方便，并可形成非常简洁可重用代码。</p>
</blockquote>
<h2 id="场景" tabindex="-1"><a class="header-anchor" href="#场景"><span>场景</span></a></h2>
<p>模型可能在多个 <em>场景</em> 下使用，例如 <code v-pre>User</code> 模块可能会在收集用户登录输入，也可能会在用户注册时使用。在不同的场景下，模型可能会使用不同的业务规则和逻辑，例如 <code v-pre>email</code> 属性在注册时强制要求有，但在登陆时不需要。</p>
<p>模型使用 [[yii\base\Model::scenario]] 属性保持使用场景的跟踪，默认情况下，模型支持一个名为 <code v-pre>default</code> 的场景，如下展示两种设置场景的方法：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 场景作为属性来设置</span></span>
<span class="line"><span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">scenario</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'login'</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 场景通过构造初始化配置来设置</span></span>
<span class="line"><span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'scenario'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'login'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>默认情况下，模型支持的场景由模型中申明的 <a href="#4eddee">验证规则</a> 来决定，但你可以通过覆盖 [[yii\base\Model::scenarios()]] 方法来自定义行为，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>models</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">User</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">const</span> <span class="token constant">SCENARIO_LOGIN</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'login'</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">const</span> <span class="token constant">SCENARIO_REGISTER</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'register'</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">scenarios</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token keyword static-context">self</span><span class="token operator">::</span><span class="token constant">SCENARIO_LOGIN</span> <span class="token operator">=></span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token keyword static-context">self</span><span class="token operator">::</span><span class="token constant">SCENARIO_REGISTER</span> <span class="token operator">=></span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Info: 在上述和下述的例子中，模型类都是继承 [[yii\db\ActiveRecord]]，因为多场景的使用通常发生在 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/db-active-record.md" target="_blank" rel="noopener noreferrer">Active Record</a> 类中。</p>
</blockquote>
<p><code v-pre>scenarios()</code> 方法返回一个数组，数组的键为场景名，值为对应的 <em>活动属性</em>。活动属性可被 <a href="#fa5663">块赋值</a> 并遵循 <a href="#4eddee">验证规则</a> 在上述例子中，<code v-pre>username</code> 和 <code v-pre>password</code> 在 <code v-pre>login</code> 场景中启用，在 <code v-pre>register</code> 场景中, 除了 <code v-pre>username</code> and <code v-pre>password</code> 外 <code v-pre>email</code> 也被启用。</p>
<p><code v-pre>scenarios()</code> 方法默认实现会返回所有 [[yii\base\Model::rules()]] 方法申明的验证规则中的场景，当覆盖 <code v-pre>scenarios()</code> 时，如果你想在默认场景外使用新场景，可以编写类似如下代码：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>models</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>ActiveRecord</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">User</span> <span class="token keyword">extends</span> <span class="token class-name">ActiveRecord</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">const</span> <span class="token constant">SCENARIO_LOGIN</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'login'</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">const</span> <span class="token constant">SCENARIO_REGISTER</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'register'</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">scenarios</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$scenarios</span> <span class="token operator">=</span> <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">scenarios</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token variable">$scenarios</span><span class="token punctuation">[</span><span class="token keyword static-context">self</span><span class="token operator">::</span><span class="token constant">SCENARIO_LOGIN</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token variable">$scenarios</span><span class="token punctuation">[</span><span class="token keyword static-context">self</span><span class="token operator">::</span><span class="token constant">SCENARIO_REGISTER</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$scenarios</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>场景特性主要在 <a href="#4eddee">验证</a> 和 <a href="#fa5663">属性块赋值</a> 中使用。你也可以用于其他目的，例如，可基于不同的场景定义不同的 <a href="#ec35f7">属性标签</a>。</p>
<h2 id="验证规则" tabindex="-1"><a class="header-anchor" href="#验证规则"><span>验证规则</span></a></h2>
<p>当模型接收到终端用户输入的数据，数据应当满足某种规则（称为 <em>验证规则</em>, 也称为 <em>业务规则</em>）。例如假定 <code v-pre>ContactForm</code> 模型，你可能想确保所有属性不为空且 <code v-pre>email</code> 属性包含一个有效的邮箱地址，如果某个属性的值不满足对应的业务规则，相应的错误信息应显示，以帮助用户修正错误。</p>
<p>可调用 [[yii\base\Model::validate()]] 来验证接收到的数据，该方法使用[[yii\base\Model::rules()]] 申明的验证规则来验证每个相关属性，如果没有找到错误，会返回 true，否则它会将错误保存在 [[yii\base\Model::errors]] 属性中并返回 <code v-pre>false</code>，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>app<span class="token punctuation">\</span>models<span class="token punctuation">\</span>ContactForm</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 用户输入数据赋值到模型属性</span></span>
<span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">attributes</span> <span class="token operator">=</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">request</span><span class="token operator">-></span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'ContactForm'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$model</span><span class="token operator">-></span><span class="token function">validate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// 所有输入数据都有效 all inputs are valid</span></span>
<span class="line"><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// 验证失败：$errors 是一个包含错误信息的数组</span></span>
<span class="line">    <span class="token variable">$errors</span> <span class="token operator">=</span> <span class="token variable">$model</span><span class="token operator">-></span><span class="token property">errors</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>通过覆盖 [[yii\base\Model::rules()]] 方法指定模型属性应该满足的规则来申明模型相关验证规则。下述例子显示 <code v-pre>ContactForm</code> 模型申明的验证规则：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">rules</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token comment">// name, email, subject 和 body 属性必须有值</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'name'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'subject'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'body'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'required'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"></span>
<span class="line">        <span class="token comment">// email 属性必须是一个有效的电子邮箱地址</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>一条规则可用来验证一个或多个属性，一个属性可对应一条或多条规则。更多关于如何申明验证规则的详情请参考 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/input-validation.md" target="_blank" rel="noopener noreferrer">验证输入</a> 一节.</p>
<p>有时你想一条规则只在某个 <a href="#c93165">场景</a> 下应用，为此你可以指定规则的 <code v-pre>on</code> 属性，如下所示:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">rules</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token comment">// 在"register" 场景下 username, email 和 password 必须有值</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'on'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'register'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"></span>
<span class="line">        <span class="token comment">// 在 "login" 场景下 username 和 password 必须有值</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'on'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'login'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如果没有指定 <code v-pre>on</code> 属性，规则会在所有场景下应用，在当前[[yii\base\Model::scenario]] 下应用的规则称之为 <em>活动规则</em>。</p>
<p>一个属性只会属于 <code v-pre>scenarios()</code> 中定义的活动属性且在 <code v-pre>rules()</code> 申明对应一条或多条活动规则的情况下被验证。</p>
<h2 id="块赋值" tabindex="-1"><a class="header-anchor" href="#块赋值"><span>块赋值</span></a></h2>
<p>块赋值只用一行代码将用户所有输入填充到一个模型，非常方便，它直接将输入数据对应填充到 [[yii\base\Model::attributes]] 属性。以下两段代码效果是相同的，都是将终端用户输入的表单数据赋值到 <code v-pre>ContactForm</code> 模型的属性，明显地前一段块赋值的代码比后一段代码简洁且不易出错。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>app<span class="token punctuation">\</span>models<span class="token punctuation">\</span>ContactForm</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">attributes</span> <span class="token operator">=</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">request</span><span class="token operator">-></span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'ContactForm'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>app<span class="token punctuation">\</span>models<span class="token punctuation">\</span>ContactForm</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$data</span> <span class="token operator">=</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">request</span><span class="token operator">-></span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'ContactForm'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">name</span> <span class="token operator">=</span> <span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$data</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'name'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$data</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'name'</span><span class="token punctuation">]</span> <span class="token punctuation">:</span> <span class="token constant">null</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">email</span> <span class="token operator">=</span> <span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$data</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'email'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$data</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'email'</span><span class="token punctuation">]</span> <span class="token punctuation">:</span> <span class="token constant">null</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">subject</span> <span class="token operator">=</span> <span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$data</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'subject'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$data</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'subject'</span><span class="token punctuation">]</span> <span class="token punctuation">:</span> <span class="token constant">null</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">body</span> <span class="token operator">=</span> <span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$data</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'body'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token variable">$data</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'body'</span><span class="token punctuation">]</span> <span class="token punctuation">:</span> <span class="token constant">null</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="安全属性" tabindex="-1"><a class="header-anchor" href="#安全属性"><span>安全属性</span></a></h3>
<p>块赋值，只应用在模型当前 [[yii\base\Model::scenario]] 场景[[yii\base\Model::scenarios()]] 方法列出的，称之为 <em>安全属性</em> 的属性上，例如，如果<code v-pre>User</code> 模型申明以下场景，当当前场景为 <code v-pre>login</code> 时候，只有 <code v-pre>username</code> and <code v-pre>password</code> 可被块赋值，其他属性不会被赋值。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">scenarios</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'login'</span> <span class="token operator">=></span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token string single-quoted-string">'register'</span> <span class="token operator">=></span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'email'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Info: 块赋值只应用在安全属性上，因为你想控制哪些属性会被终端用户输入数据所修改，例如，如果 <code v-pre>User</code> 模型有一个 <code v-pre>permission</code> 属性对应用户的权限，你可能只想让这个属性在后台界面被管理员修改。</p>
</blockquote>
<p>由于默认 [[yii\base\Model::scenarios()]] 的实现会返回 [[yii\base\Model::rules()]] 所有属性和数据，如果不覆盖这个方法，表示所有只要出现在活动验证规则中的属性都是安全的。</p>
<p>为此，提供一个特别的别名为 <code v-pre>safe</code> 的验证器来申明哪些属性是安全的不需要被验证，如下示例的规则申明 <code v-pre>title</code> 和 <code v-pre>description</code> 都为安全属性。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">rules</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'title'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'description'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'safe'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="非安全属性" tabindex="-1"><a class="header-anchor" href="#非安全属性"><span>非安全属性</span></a></h3>
<p>如上所述，[[yii\base\Model::scenarios()]] 方法提供两个用处：定义哪些属性应被验证，定义哪些属性安全。在某些情况下，你可能想验证一个属性但不想让他是安全的，可在 <code v-pre>scenarios()</code> 方法中属性名加一个惊叹号 <code v-pre>!</code>。例如像如下的 <code v-pre>secret</code> 属性。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">scenarios</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'login'</span> <span class="token operator">=></span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'!secret'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>当模型在 <code v-pre>login</code> 场景下，三个属性都会被验证，但只有 <code v-pre>username</code>和 <code v-pre>password</code> 属性会被块赋值，要对 <code v-pre>secret</code> 属性赋值，必须明确对它赋值，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">secret</span> <span class="token operator">=</span> <span class="token variable">$secret</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>在 <code v-pre>rules()</code> 方法中也可以这样做：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">rules</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'username'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'password'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'!secret'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'required'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'on'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'login'</span><span class="token punctuation">]</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在这种情况下，属性 <code v-pre>username</code>, <code v-pre>password</code> 和 <code v-pre>secret</code> 是必须的，但是 <code v-pre>secret</code> 必须被明确指定。</p>
<h2 id="数据导出" tabindex="-1"><a class="header-anchor" href="#数据导出"><span>数据导出</span></a></h2>
<p>模型通常要导出成不同格式，例如，你可能想将模型的一个集合转成 JSON 或 Excel 格式，导出过程可分解为两个步骤：</p>
<ul>
<li>模型转换成数组；</li>
<li>数组转换成所需要的格式。</li>
</ul>
<p>你只需要关注第一步，因为第二步可被通用的数据转换器，如 [[yii\web\JsonResponseFormatter]] 来完成。</p>
<p>将模型转换为数组最简单的方式是使用 [[yii\base\Model::attributes]] 属性，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$post</span> <span class="token operator">=</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>app<span class="token punctuation">\</span>models<span class="token punctuation">\</span>Post</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$array</span> <span class="token operator">=</span> <span class="token variable">$post</span><span class="token operator">-></span><span class="token property">attributes</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p>[[yii\base\Model::attributes]] 属性会返回 <em>所有</em> [[yii\base\Model::attributes()]] 申明的属性的值。</p>
<p>更灵活和强大的将模型转换为数组的方式是使用 [[yii\base\Model::toArray()]] 方法，它的行为默认和 [[yii\base\Model::attributes]] 相同，但是它允许你选择指定 <em>字段</em> 的数据项放入到结果数组中，并同时被格式化。实际上，它是导出模型到 RESTful 网页服务开发的默认方法，详情请参阅 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/rest-response-formatting.md" target="_blank" rel="noopener noreferrer">响应格式</a>。</p>
<h3 id="字段" tabindex="-1"><a class="header-anchor" href="#字段"><span>字段</span></a></h3>
<p>字段是模型通过调用 [[yii\base\Model::toArray()]] 生成的数组的单元名。</p>
<p>默认情况下，字段名对应属性名，但是你可以通过覆盖 [[yii\base\Model::fields()]] 或 [[yii\base\Model::extraFields()]] 方法来改变这种行为，两个方法都返回一个字段定义列表，<code v-pre>fields()</code> 方法定义的字段是默认字段，表示 <code v-pre>toArray()</code> 方法默认会返回这些字段。<code v-pre>extraFields()</code> 方法定义额外可用字段，通过 <code v-pre>toArray()</code> 方法指定 <code v-pre>$expand</code> 参数来返回这些额外可用字段。例如，如下代码会返回 <code v-pre>fields()</code> 方法定义的所有字段和 <code v-pre>extraFields()</code> 方法定义的 <code v-pre>prettyName</code> and <code v-pre>fullAddress</code> 字段。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$array</span> <span class="token operator">=</span> <span class="token variable">$model</span><span class="token operator">-></span><span class="token function">toArray</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'prettyName'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'fullAddress'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>可通过覆盖 <code v-pre>fields()</code> 来增加、删除、重命名和重定义字段，<code v-pre>fields()</code> 方法返回值应为数组， 数组的键为字段名，数组的值为对应的可为属性名或匿名函数返回的字段定义对应的值。特使情况下，如果字段名和属性定义名相同，可以省略数组键，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 明确列出每个字段，特别用于你想确保数据表或模型</span></span>
<span class="line"><span class="token comment">// 属性改变不会导致你的字段改变(保证后端的API兼容)。</span></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">fields</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token comment">// 字段名和属性名相同</span></span>
<span class="line">        <span class="token string single-quoted-string">'id'</span><span class="token punctuation">,</span></span>
<span class="line"></span>
<span class="line">        <span class="token comment">// 字段名为 "email"，对应属性名为 "email_address"</span></span>
<span class="line">        <span class="token string single-quoted-string">'email'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'email_address'</span><span class="token punctuation">,</span></span>
<span class="line"></span>
<span class="line">        <span class="token comment">// 字段名为 "name", 值通过PHP代码返回</span></span>
<span class="line">        <span class="token string single-quoted-string">'name'</span> <span class="token operator">=></span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">first_name</span> <span class="token operator">.</span> <span class="token string single-quoted-string">' '</span> <span class="token operator">.</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">last_name</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 过滤掉一些字段，特别用于</span></span>
<span class="line"><span class="token comment">// 你想继承父类实现并不想用一些敏感字段</span></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">fields</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$fields</span> <span class="token operator">=</span> <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">fields</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// 去掉一些包含敏感信息的字段</span></span>
<span class="line">    <span class="token keyword">unset</span><span class="token punctuation">(</span><span class="token variable">$fields</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'auth_key'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token variable">$fields</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'password_hash'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token variable">$fields</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'password_reset_token'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">return</span> <span class="token variable">$fields</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Warning: 由于模型的所有属性会被包含在导出数组，最好检查数据确保没包含敏感数据，如果有敏感数据，应覆盖 <code v-pre>fields()</code> 方法过滤掉，在上述列子中，我们选择过滤掉 <code v-pre>auth_key</code>, <code v-pre>password_hash</code> and <code v-pre>password_reset_token</code>。</p>
</blockquote>
<h2 id="最佳实践" tabindex="-1"><a class="header-anchor" href="#最佳实践"><span>最佳实践</span></a></h2>
<p>模型是代表业务数据、规则和逻辑的中心地方，通常在很多地方重用，在一个设计良好的应用中，模型通常比控制器的代码多。</p>
<p>归纳起来，模型</p>
<ul>
<li>可包含属性来展示业务数据；</li>
<li>可包含验证规则确保数据有效和完整；</li>
<li>可包含方法实现业务逻辑；</li>
<li>不应直接访问请求，session 和其他环境数据，这些数据应该由控制器传入到模型；</li>
<li>应避免嵌入 HTML 或其他展示代码，这些代码最好在视图中处理；</li>
<li>单个模型中避免太多的场景。</li>
</ul>
<p>在开发大型复杂系统时应经常考虑最后一条建议，在这些系统中，模型会很大并在很多地方使用，因此会包含需要规则集和业务逻辑，最后维护这些模型代码成为一个噩梦，因为一个简单修改会影响好多地方，为确保模型好维护，最好使用以下策略：</p>
<ul>
<li>定义可被多个应用主体或模块共享的模型基类集合。这些模型类应包含通用的最小规则集合和逻辑；</li>
<li>在每个使用模型的应用主体或模块中，通过继承对应的模型基类来定义具体的模型类，具体模型类包含应用主体或模块指定的规则和逻辑。</li>
</ul>
<p>例如，在 <a href="https://learnku.com/docs/yii-framework/2.0.x/frame-structure/11994" target="_blank" rel="noopener noreferrer">高级应用模板</a>，你可以定义一个模型基类 <code v-pre>common\models\Post</code>，然后在前台应用中，定义并使用一个继承它的具体模型类 <code v-pre>frontend\models\Post</code>，在后台应用中可以类似地定义 <code v-pre>backend\models\Post</code>。通过这种策略，你清楚 <code v-pre>frontend\models\Post</code> 只对应前台应用，如果你修改它，就无需担忧修改会影响后台应用。</p>
<blockquote>
<p>💖喜欢本文档的，欢迎点赞、收藏、留言或转发，谢谢支持！<br>
作者邮箱：zhuzixian520@126.com，github地址：<a href="https://github.com/zhuzixian520" target="_blank" rel="noopener noreferrer">github.com/zhuzixian520</a></p>
</blockquote>
</div></template>


